package com.liuya.safe.policy.controller;

import com.liuya.common.SpringUtils;
import com.liuya.common.StringUtil;
import com.liuya.common.WebUtil;
import com.liuya.base.BaseController;

import com.liuya.safe.util.AuthUtil;
import com.liuya.safe.util.Constants;
import com.liuya.safe.exception.AuthException;
import com.liuya.safe.policy.handler.AbstractPolicyDesignHandler;
import com.liuya.safe.policy.service.QueryManagerService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.liuya.db.sql.xml.BinaryExpr;
import com.liuya.db.sql.xml.ContextValue;
import com.liuya.db.sql.xml.DefineVariable;
import com.liuya.db.sql.xml.ExprGroup;
import com.liuya.db.sql.xml.ExprGroupTypeItem;
import com.liuya.db.sql.xml.Formula;
import com.liuya.db.sql.xml.HintValue;
import com.liuya.db.sql.xml.InExpr;
import com.liuya.db.sql.xml.IsNotNullExpr;
import com.liuya.db.sql.xml.IsNullExpr;
import com.liuya.db.sql.xml.NotInExpr;
import com.liuya.db.sql.xml.Operator;
import com.liuya.db.sql.xml.QueryRef;
import com.liuya.db.sql.xml.SimpleValue;
import com.liuya.db.sql.xml.UserValue;
import com.liuya.db.sql.xml.Variable;
import com.liuya.db.sql.xml.Variable1;
import com.liuya.db.sql.xml.Variable2;
import com.liuya.db.sql.xml.types.FormulaTypeOperatorType;
import com.liuya.db.sql.xml.types.FormulaTypeTypeType;
import com.liuya.db.sql.xml.types.SimpleOperatorType;
import com.liuya.db.sql.xml.types.SimpleValueTypeTypeType;
import org.springframework.ui.ModelMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class AbstractPolicyDesignController extends BaseController {

    private static final Log log = LogFactory.getLog(AbstractPolicyDesignController.class);

    public String editPolicy(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        return handler.getDesignMainPage();
    }

    public String designVariables(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        DefineVariable[] variables = handler.getVariables();
        req.setAttribute("variables", variables);

        return handler.getVariablesPage();
    }

    public String designExpr(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        return handler.getExprTreePage();
    }

    public String getVariable(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        DefineVariable[] variables = handler.getVariables();
        int index = WebUtil.getIntParameter(req, "index", 0);

        DefineVariable var = variables[index];
        String gotoPage = getVariableDefinePage(var);

        String[] userFields = AuthUtil.getUserType(req).getUserMetadata().getUserFields();
//        req.setAttribute("userFields", ReflectUtil.reflectJavaBeanFieldNames(SafeUser.class).toArray());

        req.setAttribute("userFields", userFields);
        req.setAttribute("variable", var);

        return gotoPage;
    }

    public String newVariable(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String gotoPage = getVariableDefinePage(req);
        String[] userFields = AuthUtil.getUserType(req).getUserMetadata().getUserFields();
//        req.setAttribute("userFields", ReflectUtil.reflectJavaBeanFieldNames(SafeUser.class).toArray());

        req.setAttribute("userFields", userFields);

        return gotoPage;
    }

    public String returnMain(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String gotoPage = handler.getManagePage();

        // remove handler from session
        req.getSession().removeAttribute(getPolicyHandlerAttributeKey(req));

        return "redirect:/" + gotoPage;
    }

    public String exprContextMenu(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String nodeId = req.getParameter("nodeId");
        Object obj = handler.getExprItem(nodeId);
        req.setAttribute("item", obj);
        return handler.getExprContextMenuPage();
    }

    public String loadExprForm(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String nodeId = req.getParameter("nodeId");
        // String pId=req.getParameter( "pId" );
        String exprType = req.getParameter("exprType");

        if (!StringUtil.isEmpty(nodeId)) {
            Object obj = handler.getExprItem(nodeId);
            ExprGroupTypeItem item = (ExprGroupTypeItem) obj;
            req.setAttribute("item", item);

            Object choiceValue = item.getChoiceValue();
            if (choiceValue instanceof BinaryExpr) {
                exprType = "binary";
            } else if (choiceValue instanceof InExpr || choiceValue instanceof NotInExpr) {
                exprType = "in";
            } else if (choiceValue instanceof IsNullExpr || choiceValue instanceof IsNotNullExpr) {
                exprType = "null";
            }
        }

        if ("binary".equals(exprType)) {
            return handler.getBinaryExprPage();
        } else if ("in".equals(exprType)) {
            return handler.getInExprPage();
        } else if ("null".equals(exprType)) {
            return handler.getNullExprPage();
        }
        return "";
    }

    public Object deleteVariable(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        int id = WebUtil.getIntParameter(req, "index", 0);
        handler.deleteVariable(id);

        updateHandler(req, handler);
        return true;
    }

    public Object addVariable(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        DefineVariable var = getDefineVariable(req);

        handler.addVariable(var);
        updateHandler(req, handler);
        return true;
    }

    public boolean updateVariable(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        DefineVariable var = getDefineVariable(req);

        int varIndex = WebUtil.getIntParameter(req, "index", -1);
        handler.updateVariable(varIndex, var);
        updateHandler(req, handler);
        return true;
    }

    public String loadExprTree(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        ExprGroup exprGroup = handler.getExprGroup();
        String xml = handler.getExpressionTree(exprGroup);
        if (log.isDebugEnabled()) {
            log.debug("Expression tree json: " + xml);
        }
        return xml;
    }

    public boolean addExprChildExprGroup(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String nodeId = req.getParameter("nodeId");
        String type = req.getParameter("type");

        handler.addExprChildExprGroup(nodeId, type);
        updateHandler(req, handler);
        return true;
    }

    public boolean editExprGroup(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String nodeId = req.getParameter("nodeId");
        String type = req.getParameter("type");

        handler.editExprGroup(nodeId, type);
        updateHandler(req, handler);
        return true;
    }

    public boolean deleteExpr(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String nodeId = req.getParameter("nodeId");

        handler.deleteExpr(nodeId);
        updateHandler(req, handler);
        return true;
    }

    public boolean addExprChild(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String pId = req.getParameter("pId");
        String exprType = req.getParameter("exprType");

        if ("binary".equals(exprType)) {
            BinaryExpr expr = getBinaryExpr(req);

            handler.addBinaryExpr(expr, pId);
        } else if ("null".equals(exprType)) {
            Variable var = getVariable(req);
            String operator = req.getParameter("operator");

            handler.addNullExpr(var, operator, pId);
        } else if ("in".equals(exprType)) {
            Variable var1 = getVariable(req, 1);
            Variable var2 = getVariable(req, 2);
            String operator = req.getParameter("operator");

            handler.addInExpr(var1, var2, operator, pId);
        }
        updateHandler(req, handler);
        return true;
    }

    public boolean editExpr(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        String nodeId = req.getParameter("nodeId");
        String exprType = req.getParameter("exprType");

        if ("binary".equals(exprType)) {
            BinaryExpr expr = getBinaryExpr(req);

            handler.editBinaryExpr(expr, nodeId);
        } else if ("null".equals(exprType)) {
            Variable var = getVariable(req);

            String operator = req.getParameter("operator");
            handler.editNullExpr(var, operator, nodeId);
        } else if ("in".equals(exprType)) {
            Variable var1 = getVariable(req, 1);
            Variable var2 = getVariable(req, 2);
            String operator = req.getParameter("operator");

            handler.editInExpr(var1, var2, operator, nodeId);
        }
        updateHandler(req, handler);
        return true;
    }

    public boolean save(HttpServletRequest req, HttpServletResponse rep, ModelMap modelMap){
        String oper = req.getParameter("oper");
        AbstractPolicyDesignHandler handler = getPolicyHandler(req);
        req.setAttribute("handler", handler);

        // remove handler from session
        req.getSession().removeAttribute(getPolicyHandlerAttributeKey(req));

        String id = WebUtil.getStringParameter(req, "id", "-23");
        handler.save(id);
        updateHandler(req, handler);
        return true;
    }

    public abstract AbstractPolicyDesignHandler createPolicyHandler(HttpServletRequest req);

    public abstract String getPolicyHandlerAttributeKey(HttpServletRequest req);

    private AbstractPolicyDesignHandler getPolicyHandler(HttpServletRequest req) {
        String oper = req.getParameter("oper");
        String key = getPolicyHandlerAttributeKey(req);
        AbstractPolicyDesignHandler handler = (AbstractPolicyDesignHandler) req.getSession().getAttribute(key);

        if (handler == null || "loadFresh".equals(oper)) {
            handler = createPolicyHandler(req);

            // save into session, will be removed when policy is saved
            req.getSession().setAttribute(key, handler);
        }

        handler.setDesignMode();

        return handler;
    }

    private String getVariableDefinePage(HttpServletRequest req) {
        String varType = req.getParameter("type");

        if ("contextValue".equals(varType)) {
            return "policy/contextValue";
        } else if ("formula".equals(varType)) {
            return "policy/formula";
        } else if ("hintValue".equals(varType)) {
            return "policy/businessData";
        } else if ("query".equals(varType)) {
            return "policy/queryRef";
        } else if ("simpleValue".equals(varType)) {
            return "policy/simpleValue";
        } else if ("userValue".equals(varType)) {
            return "policy/userValue";
        } else if ("hintValue".equals(varType)) {
            return "policy/businessData";
        } else {
            throw new AuthException("Unknown variable type");
        }
    }

    private String getVariableDefinePage(DefineVariable var) {
        if (var.getContextValue() != null) {
            return "policy/contextValue";
        } else if (var.getFormula() != null) {
            return "policy/formula";
        } else if (var.getHintValue() != null) {
            return "policy/businessData";
        } else if (var.getQueryRef() != null) {
            return "policy/queryRef";
        } else if (var.getSimpleValue() != null) {
            return "policy/simpleValue";
        } else if (var.getUserValue() != null) {
            return "policy/userValue";
        } else {
            throw new AuthException("Unknown variable type");
        }
    }

    private Variable getVariable(HttpServletRequest req) {
        String varName = req.getParameter("varName");
        Variable var = new Variable();
        var.setName(varName);
        return var;
    }

    private Variable getVariable(HttpServletRequest req, int index) {
        String varName = req.getParameter("varName" + index);
        Variable var = new Variable();
        var.setName(varName);
        return var;
    }

    private BinaryExpr getBinaryExpr(HttpServletRequest req) {
        String varName1 = req.getParameter("varName1");
        String varName2 = req.getParameter("varName2");
        String operator = req.getParameter("operator");

        Variable1 v1 = new Variable1();
        v1.setName(varName1);
        Variable2 v2 = new Variable2();
        v2.setName(varName2);

        Operator oper = new Operator();
        oper.setSimpleOperator(SimpleOperatorType.valueOf(operator));

        BinaryExpr expr = new BinaryExpr();
        expr.setVariable1(v1);
        expr.setVariable2(v2);
        expr.setOperator(oper);

        return expr;
    }

    private DefineVariable getDefineVariable(HttpServletRequest req) {
        String type = req.getParameter("type");
        String name = req.getParameter("name");
        DefineVariable var = new DefineVariable();
        var.setName(name);

        if ("userValue".equals(type)) {
            String key = req.getParameter("key");

            UserValue userValue = new UserValue();
            userValue.setKey(key);

            var.setUserValue(userValue);
        } else if ("simpleValue".equals(type)) {
            String content = req.getParameter("content");
            String stype = req.getParameter("stype");
            SimpleValue value = new SimpleValue();
            value.setContent(content);
            value.setType(SimpleValueTypeTypeType.valueOf(stype));

            var.setSimpleValue(value);
        } else if ("contextValue".equals(type)) {
            String key = req.getParameter("key");

            ContextValue value = new ContextValue();
            value.setKey(key);

            var.setContextValue(value);
        } else if ("hintValue".equals(type)) {
            String key = req.getParameter("key");

            HintValue value = new HintValue();
            value.setKey(key);
            value.setHint(Constants.BUSINESS_DATA);

            var.setHintValue(value);
        } else if ("formula".equals(type)) {
            String operName1 = req.getParameter("operName1");
            String operName2 = req.getParameter("operName2");
            String operator = req.getParameter("operator");
            String returnType = req.getParameter("returnType");

            Variable var0 = new Variable();
            var0.setName(operName1);
            Variable var1 = new Variable();
            var1.setName(operName2);

            Formula value = new Formula();

            value.addVariable(var0);
            value.addVariable(var1);
            value.setOperator(FormulaTypeOperatorType.valueOf(operator));
            value.setType(FormulaTypeTypeType.valueOf(returnType));
            var.setFormula(value);
        } else if ("queryRef".equals(type)) {
            String id = WebUtil.getStringParameter(req, "queryId", "-999");
            QueryManagerService mng = (QueryManagerService) SpringUtils.getBean("queryManagerService");
            String queryName = mng.getQuery(id).getName();

            QueryRef value = new QueryRef();
            value.setId(id);
            value.setName(queryName);

            var.setQueryRef(value);
        }

        return var;
    }

    protected abstract void updateHandler(HttpServletRequest req, Object handler);

}
